#include <set>
#include "def.h"
#include "util.h"
#include "net.h"
#include "sync.h"
#include "gui.h"
#include "plugin.h"

#include "..\Global\globaldef.h"
#include "..\CommLib\PacketConven.h"
#include "..\CommLib\Comm.h"

#include <maya/MfnDagNode.h>
#include <maya/MTransformationMatrix.h>
#include <maya/MFnTransform.h>
#include <maya/MMatrix.h>

#include <maya/MItMeshFaceVertex.h>
#include <maya/MFloatPointArray.h>
#include <maya/MFloatArray.h>
#include <maya/MIntArray.h>
#include <maya/MPointArray.h>
#include <maya/MStringArray.h>
#include <maya/MFnMesh.h>
#include <maya/MPlug.h>
#include <maya/MItMeshPolygon.h>
#include <maya/MDagPath.h>

extern bool GetNameBuffer(tCharBuffer* pCharBuffer, MString object);
extern bool GetNameBuffer(tCharBuffer* pCharBuffer, MObject object);
extern bool IsEqual(const char* viewerString, MString& mayaString);
extern int ChannelStrToInt(MString& name);

Connection VNet::g_Client;
MString VNet::HostAddress = "Dacre-PC";
MString VNet::g_Status = "Uninitalized";

HANDLE VNet::g_hMontiorConnection = NULL;
bool VNet::g_isMonitoring = false;


#define PACKCOLOR_ARGB(a,r,g,b) ((DWORD)((((a)&0xff)<<24)|(((r)&0xff)<<16)|(((g)&0xff)<<8)|((b)&0xff)))
#define PACKCOLOR_COLORVALUE(r,g,b,a) PACKCOLOR_ARGB((DWORD)((a)*255.f),(DWORD)((r)*255.f),(DWORD)((g)*255.f),(DWORD)((b)*255.f))

void ExtractProcedrualFloatAttribute(MFnDependencyNode& procedrualDN, MString& attributeName, float* pOut) {

	MPlug valuePlug;
	float value = 0.0f;

	if(VUtil::GetObjectAttribute(procedrualDN.object(), attributeName,valuePlug)) {
	
		if(MAYA_SUCCESS(valuePlug.getValue(value))) {

			*pOut = value;
			return;
		}
	}

	*pOut = 0.0f;
}

void ExtractProcedrualColorAttribute(MFnDependencyNode& procedrualDN, MString attributeName, tVector3* pOut) {

	try {
	
		if(!pOut)
			throw tException();

		MPlug valuePlug;
		if(!VUtil::GetObjectAttribute(procedrualDN.object(), attributeName + MString("R") ,valuePlug))
			throw tException();
		if(MAYA_FAIL(valuePlug.getValue(pOut->x)))
			throw tException();

		if(!VUtil::GetObjectAttribute(procedrualDN.object(), attributeName + MString("G") ,valuePlug))
			throw tException();
		if(MAYA_FAIL(valuePlug.getValue(pOut->y)))
			throw tException();

		if(!VUtil::GetObjectAttribute(procedrualDN.object(), attributeName + MString("B") ,valuePlug))
			throw tException();
		if(MAYA_FAIL(valuePlug.getValue(pOut->z)))
			throw tException();

	}
	catch(tException exception) {

		if(pOut) {

			pOut->x = 
			pOut->y = 
			pOut->z = 0.0f;
		}
	}
}

void ExtractProcedrualPositionAttribute(MFnDependencyNode& procedrualDN, MString attributeName, tVector3* pOut) {

	try {
	
		if(!pOut)
			throw tException();

		MPlug valuePlug;
		if(!VUtil::GetObjectAttribute(procedrualDN.object(), attributeName + MString("X") ,valuePlug))
			throw tException();
		if(MAYA_FAIL(valuePlug.getValue(pOut->x)))
			throw tException();

		if(!VUtil::GetObjectAttribute(procedrualDN.object(), attributeName + MString("Y") ,valuePlug))
			throw tException();
		if(MAYA_FAIL(valuePlug.getValue(pOut->y)))
			throw tException();

		if(!VUtil::GetObjectAttribute(procedrualDN.object(), attributeName + MString("Z") ,valuePlug))
			throw tException();
		if(MAYA_FAIL(valuePlug.getValue(pOut->z)))
			throw tException();

	}
	catch(tException exception) {

		if(pOut) {

			pOut->x = 
			pOut->y = 
			pOut->z = 0.0f;
		}
	}
}

void ExtractProcedrualUintAttribute(MFnDependencyNode& procedrualDN, MString& attributeName, uint* pOut) {

	MPlug valuePlug;
	int value = 0.0f;

	if(VUtil::GetObjectAttribute(procedrualDN.object(), attributeName,valuePlug)) {
	
		if(MAYA_SUCCESS(valuePlug.getValue(value))) {

			*pOut = (uint)max(0, value);
			return;
		}
	}

	*pOut = 0;
}

/**
	Updates the samplers of the target object on the server. Assumes the target object exists
	on the server. Requires that the state of attributes in the target object are consistent with
	overall system conventions.

	@author Dacre Denny
	@param targetObject the object whose samplers are being sent to the server
	@return true if successful otherwise false
*/
//bool VNet::SendSamplers(MObject& targetObject) {
//	
//	try {
//
//		/*
//		Check basic preconditions of incoming data
//		*/
//		MString objectName;
//		if(!VUtil::IsMesh(targetObject) || !VUtil::GetObjectName(targetObject, objectName))
//			throw tException("Invalid parameter.");
//				
//		/*
//		Get sampler names
//		*/
//		MString attributeString;
//		MStringArray colorPartsArray;
//		MStringArray normalPartsArray;
//		MStringArray specularPartsArray;
//		MPlug plug;
//
//		if(!VUtil::GetObjectAttribute(targetObject, MString("liveview_color"), plug))
//			throw tException();
//		attributeString = plug.asString();
//		attributeString.split(';', colorPartsArray);
//
//		if(!VUtil::GetObjectAttribute(targetObject, MString("liveview_normal"), plug))
//			throw tException();
//		attributeString = plug.asString();
//		attributeString.split(';', normalPartsArray);
//
//		if(!VUtil::GetObjectAttribute(targetObject, MString("liveview_specular"), plug))
//			throw tException();
//		attributeString = plug.asString();
//		attributeString.split(';', specularPartsArray);
//
//		MFnMesh targetMesh(targetObject);
//		MObjectArray shaderArray;
//		if(MAYA_FAIL(targetMesh.getConnectedShaders(0, shaderArray, MIntArray())))
//			throw tException();
//
//		uint partCount = shaderArray.length();
//		if(partCount == 0)
//			throw tException();
//
//		for(uint i = 0; i < partCount; i++) {
//
//			MStringArray colorSamplersArray;
//			MStringArray normalSamplersArray;
//			MStringArray specularSamplersArray;
//			MString colorPart = colorPartsArray[i];
//			if(MAYA_FAIL(colorPart.split(',', colorSamplersArray)))
//				throw tException();
//
//			MString normalPart = normalPartsArray[i];
//			if(MAYA_FAIL(normalPart.split(',', normalSamplersArray)))
//				throw tException();
//			
//			MString specularPart = specularPartsArray[i];
//			if(MAYA_FAIL(specularPart.split(',', specularSamplersArray)))
//				throw tException();
//
//			if(colorSamplersArray.length() < 5 ||
//				normalSamplersArray.length() < 5 ||
//				specularSamplersArray.length() < 5)
//				throw tException();
//
//			for(uint j = 0; j < 5; j++) {
//
//				MString samplerName;
//				MObject samplerObject;
//				
//				/*
//				Might not be assinged which is ok, so continue if failed
//				*/
//				samplerName = colorSamplersArray[j];
//				if(VUtil::GetSamplerByName(samplerName, samplerObject)) {
//
//					if(!SendSampler(targetObject, samplerObject, i, MString("liveview_color"), false))
//						throw tException("Failed to send sampler '%s'", samplerName.asChar());
//				}
//
//				samplerName = normalSamplersArray[j];
//				if(VUtil::GetSamplerByName(samplerName, samplerObject)) {
//
//					if(!SendSampler(targetObject, samplerObject, i, MString("liveview_normal"), false))
//						throw tException("Failed to send sampler '%s'", samplerName.asChar());
//				}
//
//				samplerName = specularSamplersArray[j];
//				if(VUtil::GetSamplerByName(samplerName, samplerObject)) {
//
//					if(!SendSampler(targetObject, samplerObject, i, MString("liveview_specular"), false))
//						throw tException("Failed to send sampler '%s'", samplerName.asChar());
//				}
//			}
//		}
//
//		return true;
//
//	}catch(tException exception) {
//
//		VPlugin::HandleException(&exception);
//		return false;
//	}
//}
//
/**
	Updates the parameters of the target object on the server. Assumes the target object exists
	on the server. Requires that the state of attributes in the target object are consistent with
	overall system conventions.

	@author Dacre Denny
	@param targetObject the object whose parameters are being sent to the server
	@return true if successful otherwise false
*/
bool VNet::SendMesh_Parameters(MObject& targetObject, int instanced, int deformation, float deformationscale ) {
	
	try {

		/*
		Check basic preconditions of incoming data
		*/
		if(!VUtil::IsMesh(targetObject))
			throw tException("Invalid parameter.");

		HeaderStructs::tParametersHeader objectParameters;

		MString objectName;
		VUtil::GetObjectName(targetObject, objectName);

		objectParameters.instancable = instanced;
		objectParameters.deformation = deformation;
		objectParameters.deformationScale = deformationscale;
		GetNameBuffer(&objectParameters.target, objectName);

		/*
		Send the data and tidy up
		*/
		if(!g_Client.SendDataPacket((LPVOID)&objectParameters, sizeof(HeaderStructs::tParametersHeader), UPDATE_MESHPARAMETERS))
			throw tException("SendDataPacket() failed.");

		return true;

	}catch(tException exception) {

		VPlugin::HandleException(&exception);
		return false;
	}
}

uint GetSamplerSize(MObject& samplerObject) {

	switch(samplerObject.apiType()) {

		case MFn::kFileTexture:
			return sizeof(DataStructs::tTextureFile);
		
		case MFn::kChecker:
			return sizeof(DataStructs::tChecker);

		case MFn::kNoise:
			return sizeof(DataStructs::tNoise);

		case MFn::kGrid:
			return sizeof(DataStructs::tGrid);

		case MFn::kRamp: 
			return sizeof(DataStructs::tRamp);
	}
	return 0;
}

uint GetSamplerType(MObject& samplerObject) {

	switch(samplerObject.apiType()) {

		case MFn::kFileTexture:
			return SAMPLE_TEXTURE;
		
		case MFn::kChecker:
			return SAMPLE_CHECKER;

		case MFn::kNoise:
			return SAMPLE_NOISE;

		case MFn::kGrid:
			return SAMPLE_GRID;

		case MFn::kRamp: 
			return SAMPLE_RAMP;
	}
	return 0;
}

bool VNet::SendPart_Shader(MObject& targetObject, uint part, MString& filename, bool remove) {

	char* pHeadBytes = NULL;
	FILE* pFile = NULL;

	try {

		/*
		Check basic preconditions of incoming data
		*/
		MString objectName;
		if(!VUtil::IsMesh(targetObject) || filename.length() == 0)
			throw tException("Invalid parameter.");
		
		uint partCount = VUtil::GetMeshPartCount(targetObject);
		if(part >= partCount) 
			throw tException("Invalid range.");
	
		pFile = fopen(filename.asChar(), "rb");
		if(!pFile)
			throw tException("Failed to open shader file.");
		fseek(pFile, 0, SEEK_END);
		long size = ftell(pFile);
		if(size == 0)
			throw tException("Shader file contains nothing.");
		fseek(pFile, 0, SEEK_SET);

		uint sendSize = sizeof(HeaderStructs::tShaderHeader) + size;
		pHeadBytes = new char [sendSize]; 
		if(!pHeadBytes)
			throw tException("Memory error.");

		HeaderStructs::tShaderHeader* pHeader = (HeaderStructs::tShaderHeader*)pHeadBytes;
		GetNameBuffer(&pHeader->filename, filename);
		GetNameBuffer(&pHeader->target, objectName);
		pHeader->targetPart = part;
		pHeader->filesize = size;

		char* pWriter = pHeadBytes + sizeof(HeaderStructs::tShaderHeader);
		fread(pWriter, size, 1, pFile);
		fclose(pFile);

		/*
		Send the data and tidy up
		*/
 		if(!g_Client.SendDataPacket(pHeadBytes, sendSize, UPDATE_MESHSHADER))
			throw tException("SendDataPacket() failed.");
		delete [] pHeadBytes;

		return true;

	}catch(tException exception) {

		VPlugin::HandleException(&exception);
		if(pHeadBytes)
			delete[] pHeadBytes;
		fclose(pFile);
		return false;
	}
}



bool VNet::SendPart_ChannelChange(MObject& targetObject, uint part, MString& channel, bool remove) {

	try {

		/*
		Check basic preconditions of incoming data
		*/
		if(!VUtil::IsMesh(targetObject))
			throw tException("Invalid parameter(s).");
		
		int channelIndex = ChannelStrToInt(channel);
		if(channelIndex < 0 || channelIndex > 3)
			throw tException("Invalid channel.");

		MString targetName;
		if(!VUtil::GetObjectName(targetObject, targetName))
			throw tException("Failed to get target name.");

		HeaderStructs::tChannelHeader sendData;
		GetNameBuffer(&sendData.target, targetName);
		sendData.part = part;
		sendData.channel = channelIndex;
		sendData.remove = remove;

		/*
		Send the sampler information and tidy up
		*/
		if(!g_Client.SendDataPacket((LPVOID)&sendData, sizeof(HeaderStructs::tChannelHeader), UPDATE_MESHCHANNEL))
			throw tException("Failed to send surface texture.");

		return true;

	}catch(tException exception) {
	
		return false;
	}
}

bool VNet::SendPart_SamplerChange(MObject& targetObject, MObject& samplerObject, uint part, MString& channel, bool remove) {

	try {

		/*
		Check basic preconditions of incoming data
		*/
		if(!VUtil::IsMesh(targetObject) || !VUtil::IsTextureSupported(samplerObject))
			throw tException("Invalid parameter(s).");
		
		int channelIndex = ChannelStrToInt(channel);
		if(channelIndex < 0 || channelIndex > 3)
			throw tException("Invalid channel.");

		MString targetName;
		if(!VUtil::GetObjectName(targetObject, targetName))
			throw tException("Failed to get target name.");

		HeaderStructs::tSamplerHeader sendData;
		GetNameBuffer(&sendData.target, targetName);
		GetNameBuffer(&sendData.sampler, samplerObject);
		sendData.part = part;
		sendData.type = GetSamplerType(samplerObject);
		sendData.channel = channelIndex;
		sendData.remove = remove;

		/*
		Send the sampler information and tidy up
		*/
		if(!g_Client.SendDataPacket((LPVOID)&sendData, sizeof(HeaderStructs::tSamplerHeader), UPDATE_MESHSAMPLER))
			throw tException("Failed to send surface texture.");

		return true;

	}catch(tException exception) {
	
		return false;
	}
}

bool VNet::SendScene_SamplerChange(MObject& samplerObject)  {

	char* pHeadBytes = NULL;
	FILE* pFile = NULL;
	
	try {

		/*
		Check basic preconditions of incoming data
		*/
		if(!VUtil::IsTextureSupported(samplerObject))
			throw tException("Invalid parameter(s).");
		
		uint sendSize = 0;
		uint samplerType = GetSamplerType(samplerObject);
		if(samplerType == SAMPLE_TEXTURE) {

			MString textureFilename;
			if(!VUtil::GetTextureFilename(samplerObject, textureFilename))
				throw tException("Failed to extract filename.");
			
			pFile = fopen(textureFilename.asChar(), "rb");
			if(!pFile)
				throw tException("Failed to open sampler file.");
			fseek(pFile, 0, SEEK_END);
			long size = ftell(pFile);
			if(size == 0)
				throw tException("Mask file contains nothing.");
			fseek(pFile, 0, SEEK_SET);

			sendSize = sizeof(HeaderStructs::tSurfaceTextureHeader) + sizeof(DataStructs::tTextureFile) + size;
			pHeadBytes = new char [sendSize]; 
			if(!pHeadBytes)
				throw tException("Memory error.");

			char* pWriter = pHeadBytes;
			DataStructs::tTextureFile* pHeader = (DataStructs::tTextureFile*)pWriter;
			GetNameBuffer(&pHeader->filename, textureFilename);			
			pHeader->size = size;
			pWriter += sizeof(DataStructs::tTextureFile);

			fread(pWriter, size, 1, pFile);
			fclose(pFile);
		}
		else {

			uint samplerSize = GetSamplerSize(samplerObject);
			if(samplerSize == 0)
				throw tException();

			/*
			Allocate the data Buffer/stream that will contain the sampler specific information that
			is being sent to the server
			*/
			sendSize = sizeof(HeaderStructs::tSurfaceTextureHeader) + samplerSize;
			pHeadBytes = new char [ sendSize ];
			if(!pHeadBytes)
				throw tException();
			
			char* pWriter = pHeadBytes;

			/*
			Write sampler specific information into this stream depending on the type that was detected
			*/
			MFnDependencyNode samplerNode(samplerObject);
			switch(samplerType) {
				
				case SAMPLE_CHECKER: {	

					DataStructs::tChecker* pSampler = (DataStructs::tChecker*)pWriter;
					ExtractProcedrualFloatAttribute(samplerNode, MString("contrast"), (float*)&pSampler->contrast);
					ExtractProcedrualColorAttribute(samplerNode, MString("color1"), (tVector3*)&pSampler->color_a);
					ExtractProcedrualColorAttribute(samplerNode, MString("color2"), (tVector3*)&pSampler->color_b);
				
				}break;

				case SAMPLE_NOISE: {
					
					DataStructs::tNoise* pSampler = (DataStructs::tNoise*)pWriter;
					ExtractProcedrualUintAttribute(samplerNode, MString("depthMax"), (uint*)&pSampler->octaves);
					ExtractProcedrualFloatAttribute(samplerNode, MString("amplitude"), (float*)&pSampler->amplitude);
					ExtractProcedrualFloatAttribute(samplerNode, MString("frequencyRatio"), (float*)&pSampler->scale);
					ExtractProcedrualFloatAttribute(samplerNode, MString("threshold"), (float*)&pSampler->offset);
				}break;

				case SAMPLE_GRID: {

					DataStructs::tGrid* pSampler = (DataStructs::tGrid*)pWriter;
					ExtractProcedrualFloatAttribute(samplerNode, MString("contrast"), (float*)&pSampler->contrast);
					ExtractProcedrualFloatAttribute(samplerNode, MString("uWidth"), (float*)&pSampler->width);
					ExtractProcedrualFloatAttribute(samplerNode, MString("vWidth"), (float*)&pSampler->height);
					ExtractProcedrualColorAttribute(samplerNode, MString("lineColor"), (tVector3*)&pSampler->color_a);
					ExtractProcedrualColorAttribute(samplerNode, MString("fillerColor"), (tVector3*)&pSampler->color_b);
				
				}break;

				case SAMPLE_RAMP: {

					DataStructs::tRamp* pSampler = (DataStructs::tRamp*)pWriter;
					ExtractProcedrualFloatAttribute(samplerNode, MString("uWave"), (float*)&pSampler->wave);
				}break;
				
				default:
					throw tException("Unsupported surface texture type (%i).", samplerType);
			}
		}

		/*
		Send the sampler information and tidy up
		*/
		if(!g_Client.SendDataPacket((LPVOID)pHeadBytes, sendSize, UPDATE_INSTANCESAMPLER))
			throw tException("Failed to send instance sampler.");

		delete [] pHeadBytes;
		return true;

	}catch(tException exception) {

		if(pHeadBytes)
			delete [] pHeadBytes;
	
		if(pFile)
			fclose(pFile);
		return false;
	}
}

//bool VNet::SendScene_Sampler(MObject& samplerObject) {
//
//	char* pHeadBytes = NULL;
//	FILE* pFile = NULL;
//	
//	try {
//
//		/*
//		Check basic preconditions of incoming data
//		*/
//		if(!VUtil::IsTextureSupported(samplerObject))
//			throw tException("Invalid parameter.");
//		
//		uint sendSize = 0;
//		uint samplerType = GetSamplerType(samplerObject);
//		if(samplerType == SAMPLE_TEXTURE) {
//
//			MString textureFilename;
//			if(!VUtil::GetTextureFilename(samplerObject, textureFilename))
//				throw tException("Failed to extract filename.");
//			
//			pFile = fopen(textureFilename.asChar(), "rb");
//			if(!pFile)
//				throw tException("Failed to open sampler file.");
//			fseek(pFile, 0, SEEK_END);
//			long size = ftell(pFile);
//			if(size == 0)
//				throw tException("Mask file contains nothing.");
//			fseek(pFile, 0, SEEK_SET);
//
//			sendSize = sizeof(DataStructs::tTextureFile) + size;
//			pHeadBytes = new char [sendSize]; 
//			if(!pHeadBytes)
//				throw tException("Memory error.");
//
//			char* pWriter = pHeadBytes;
//			DataStructs::tTextureFile* pHeader = (DataStructs::tTextureFile*)pWriter;
//			GetNameBuffer(&pHeader->filename, textureFilename);			
//			pHeader->size = size;
//			pWriter += sizeof(DataStructs::tTextureFile);
//
//			fread(pWriter, size, 1, pFile);
//			fclose(pFile);
//		}
//		else {
//
//			uint samplerSize = GetSamplerSize(samplerObject);
//			if(samplerSize == 0)
//				throw tException();
//
//			/*
//			Allocate the data Buffer/stream that will contain the sampler specific information that
//			is being sent to the server
//			*/
//			sendSize = samplerSize;
//			pHeadBytes = new char [ sendSize ];
//			if(!pHeadBytes)
//				throw tException();
//			
//			char* pWriter = pHeadBytes;
//
//			/*
//			Write sampler specific information into this stream depending on the type that was detected
//			*/
//			MFnDependencyNode samplerNode(samplerObject);
//			switch(samplerType) {
//				
//				case SAMPLE_CHECKER: {	
//
//					DataStructs::tChecker* pSampler = (DataStructs::tChecker*)pWriter;
//					ExtractProcedrualFloatAttribute(samplerNode, MString("contrast"), (float*)&pSampler->contrast);
//					ExtractProcedrualColorAttribute(samplerNode, MString("color1"), (tVector3*)&pSampler->color_a);
//					ExtractProcedrualColorAttribute(samplerNode, MString("color2"), (tVector3*)&pSampler->color_b);
//				
//				}break;
//
//				case SAMPLE_NOISE: {
//					
//					DataStructs::tNoise* pSampler = (DataStructs::tNoise*)pWriter;
//					ExtractProcedrualUintAttribute(samplerNode, MString("depthMax"), (uint*)&pSampler->octaves);
//					ExtractProcedrualFloatAttribute(samplerNode, MString("amplitude"), (float*)&pSampler->amplitude);
//					ExtractProcedrualFloatAttribute(samplerNode, MString("frequencyRatio"), (float*)&pSampler->scale);
//					ExtractProcedrualFloatAttribute(samplerNode, MString("threshold"), (float*)&pSampler->offset);
//				}break;
//
//				case SAMPLE_GRID: {
//
//					DataStructs::tGrid* pSampler = (DataStructs::tGrid*)pWriter;
//					ExtractProcedrualFloatAttribute(samplerNode, MString("contrast"), (float*)&pSampler->contrast);
//					ExtractProcedrualFloatAttribute(samplerNode, MString("uWidth"), (float*)&pSampler->width);
//					ExtractProcedrualFloatAttribute(samplerNode, MString("vWidth"), (float*)&pSampler->height);
//					ExtractProcedrualColorAttribute(samplerNode, MString("lineColor"), (tVector3*)&pSampler->color_a);
//					ExtractProcedrualColorAttribute(samplerNode, MString("fillerColor"), (tVector3*)&pSampler->color_b);
//				
//				}break;
//
//				case SAMPLE_RAMP: {
//
//					DataStructs::tRamp* pSampler = (DataStructs::tRamp*)pWriter;
//					ExtractProcedrualFloatAttribute(samplerNode, MString("uWave"), (float*)&pSampler->wave);
//				}break;
//				
//				default:
//					throw tException("Unsupported surface texture type (%i).", samplerType);
//			}
//		}
//
//		/*
//		Send the sampler information and tidy up
//		*/
//		if(!g_Client.SendDataPacket((LPVOID)pHeadBytes, sendSize, UPDATE_INSTANCESAMPLER))
//			throw tException("Failed to send instance sampler.");
//
//		delete [] pHeadBytes;
//		return true;
//
//	}catch(tException exception) {
//
//		if(pHeadBytes)
//			delete [] pHeadBytes;
//	
//		if(pFile)
//			fclose(pFile);
//		return false;
//	}
//}

bool VNet::SendInstancing_ChildMask(MObject& targetObject, MObject& childObject, MString& filename){

	char* pHeadBytes = NULL;
	FILE* pFile = NULL;

	try {

		/*
		Check basic preconditions of incoming data
		*/
		MString objectName;
		if(!VUtil::IsMesh(targetObject) || !VUtil::IsMesh(childObject) || filename.length() == 0)
			throw tException("Invalid parameter.");
		
		pFile = fopen(filename.asChar(), "rb");
		if(!pFile)
			throw tException("Failed to open mask file.");
		fseek(pFile, 0, SEEK_END);
		long size = ftell(pFile);
		if(size == 0)
			throw tException("Mask file contains nothing.");
		fseek(pFile, 0, SEEK_SET);

		uint sendSize = sizeof(HeaderStructs::tMaskHeader) + size;
		pHeadBytes = new char [sendSize]; 
		if(!pHeadBytes)
			throw tException("Memory error.");

		HeaderStructs::tMaskHeader* pHeader = (HeaderStructs::tMaskHeader*)pHeadBytes;
		GetNameBuffer(&pHeader->filename, filename);
		GetNameBuffer(&pHeader->target, objectName);
		pHeader->filesize = size;

		char* pWriter = pHeadBytes + sizeof(HeaderStructs::tMaskHeader);
		fread(pWriter, size, 1, pFile);
		fclose(pFile);

		/*
		Send the data and tidy up
		*/
 		if(!g_Client.SendDataPacket(pHeadBytes, sendSize, UPDATE_INSTANCEMASK))
			throw tException("SendDataPacket() failed.");
		delete [] pHeadBytes;

		return true;

	}catch(tException exception) {

		VPlugin::HandleException(&exception);
		if(pHeadBytes)
			delete[] pHeadBytes;
		fclose(pFile);
		return false;
	}
}
bool VNet::SendInstancing_ChildSampler(MObject& targetObject, MObject& childObject, MObject& sampler, MString& channel){

	char* pHeadBytes = NULL;
	
	try {

		/*
		Check basic preconditions of incoming data
		*/
		if(!VUtil::IsTextureSupported(sampler) || !VUtil::IsMesh(targetObject) || !VUtil::IsMesh(childObject))
			throw tException("Invalid parameter(s).");
		
		uint samplerType = GetSamplerType(sampler);
		uint samplerSize = GetSamplerSize(sampler);
		if(samplerSize == 0)
			throw tException();

		/*
		Allocate the data Buffer/stream that will contain the sampler specific information that
		is being sent to the server
		*/
		uint sendSize = sizeof(HeaderStructs::tInstanceChildSampler) + samplerSize;
		pHeadBytes = new char [ sendSize ];
		if(!pHeadBytes)
			throw tException();
		ZeroMemory(pHeadBytes, sendSize);
		char* pWriteBytes = pHeadBytes;

		MString targetName;
		if(!VUtil::GetObjectName(targetObject, targetName))
			throw tException("Failed to get target name.");
		
		MString childName;
		if(!VUtil::GetObjectName(childObject, childName))
			throw tException("Failed to get child name.");

		MString samplername;
		if(!VUtil::GetObjectName(sampler, samplername))
			throw tException("Failed to get sampler name.");

		/*
		Write header information about this sampler into the stream
		*/
		HeaderStructs::tInstanceChildSampler* pHeader = (HeaderStructs::tInstanceChildSampler*)pHeadBytes;
		pWriteBytes += sizeof(HeaderStructs::tInstanceChildSampler);
		GetNameBuffer(&pHeader->target, targetName);
		GetNameBuffer(&pHeader->child, childObject);
		GetNameBuffer(&pHeader->channel, channel);
		pHeader->samplerType = samplerType;

		/*
		Write sampler specific information into this stream depending on the type that was detected
		*/
		MFnDependencyNode samplerNode(sampler);
		switch(samplerType) {
			
			case SAMPLE_CHECKER: {	

				DataStructs::tChecker* pSampler = (DataStructs::tChecker*)pWriteBytes;
				ExtractProcedrualFloatAttribute(samplerNode, MString("contrast"), (float*)&pSampler->contrast);
				ExtractProcedrualColorAttribute(samplerNode, MString("color1"), (tVector3*)&pSampler->color_a);
				ExtractProcedrualColorAttribute(samplerNode, MString("color2"), (tVector3*)&pSampler->color_b);
			}break;

			case SAMPLE_NOISE: {
				
				DataStructs::tNoise* pSampler = (DataStructs::tNoise*)pWriteBytes;
				ExtractProcedrualUintAttribute(samplerNode, MString("depthMax"), (uint*)&pSampler->octaves);
				ExtractProcedrualFloatAttribute(samplerNode, MString("amplitude"), (float*)&pSampler->amplitude);
				ExtractProcedrualFloatAttribute(samplerNode, MString("frequencyRatio"), (float*)&pSampler->scale);
				ExtractProcedrualFloatAttribute(samplerNode, MString("threshold"), (float*)&pSampler->offset);
			}break;

			case SAMPLE_GRID: {

				DataStructs::tGrid* pSampler = (DataStructs::tGrid*)pWriteBytes;
				ExtractProcedrualFloatAttribute(samplerNode, MString("contrast"), (float*)&pSampler->contrast);
				ExtractProcedrualFloatAttribute(samplerNode, MString("uWidth"), (float*)&pSampler->width);
				ExtractProcedrualFloatAttribute(samplerNode, MString("vWidth"), (float*)&pSampler->height);
				ExtractProcedrualColorAttribute(samplerNode, MString("lineColor"), (tVector3*)&pSampler->color_a);
				ExtractProcedrualColorAttribute(samplerNode, MString("fillerColor"), (tVector3*)&pSampler->color_b);
			}break;

			case SAMPLE_RAMP: {

				DataStructs::tRamp* pSampler = (DataStructs::tRamp*)pWriteBytes;
				ExtractProcedrualFloatAttribute(samplerNode, MString("uWave"), (float*)&pSampler->wave);
			}break;
			
			default:
				throw tException("Unsupported instance sampler type (%i).", samplerType);
		}

		/*
		Send the sampler information and tidy up
		*/
		if(!g_Client.SendDataPacket((LPVOID)pHeadBytes, sendSize, UPDATE_INSTANCESAMPLER))
			throw tException("Failed to send surface texture.");

		delete [] pHeadBytes;
		return true;

	}catch(tException exception) {

		if(pHeadBytes)
			delete [] pHeadBytes;
	
		return false;
	}
	return true;
}
bool VNet::SendInstancing_ChildUV(MObject& targetObject, MObject& childObject, int uvchannel){

	try {

		/*
		Check basic preconditions of incoming data
		*/
		if(!VUtil::IsMesh(targetObject) || !VUtil::IsMesh(childObject) || (uvchannel < 0 || uvchannel > 4))
			throw tException("Invalid parameter(s).");
		
		MString targetName;
		if(!VUtil::GetObjectName(targetObject, targetName))
			throw tException("Failed to get target name.");
		
		MString childName;
		if(!VUtil::GetObjectName(childObject, childName))
			throw tException("Failed to get child name.");

		HeaderStructs::tInstanceChildUV sendData;
		GetNameBuffer(&sendData.target, targetName);
		GetNameBuffer(&sendData.child, childName);
		sendData.uv = uvchannel;

		/*
		Send the sampler information and tidy up
		*/
		if(!g_Client.SendDataPacket((LPVOID)&sendData, sizeof(HeaderStructs::tInstanceChildUV), UPDATE_INSTANCEUV))
			throw tException("Failed to send surface texture.");

		return true;

	}catch(tException exception) {
	
		return false;
	}
	return true;
}
bool VNet::SendInstancing_ChildChange(MObject& targetObject, MObject& childObject, bool remove){

	try {

		/*
		Check basic preconditions of incoming data
		*/
		if(!VUtil::IsMesh(targetObject) || !VUtil::IsMesh(childObject))
			throw tException("Invalid parameter(s).");
		
		MString targetName;
		MString childName;
		
		VUtil::GetObjectName(targetObject, targetName);
		VUtil::GetObjectName(childObject, childName);

		HeaderStructs::tInstanceChildChange sendData;
		GetNameBuffer(&sendData.target, targetName);
		GetNameBuffer(&sendData.child, childName);
		sendData.remove = remove;

		/*
		Send the sampler information and tidy up
		*/
		if(!g_Client.SendDataPacket((LPVOID)&sendData, sizeof(HeaderStructs::tInstanceChildChange), UPDATE_INSTANCECHANGE))
			throw tException("Failed to send instance change data.");

		return true;

	}catch(tException exception) {
	
		return false;
	}
	return true;
}

/**
	Sends data about the current scene lights to the viewer
	
	@param targetLight the light object to send
	@param remove indicates if the server should remove or add the light
	@author Dacre Denny
	@return true if successful otherwise false
*/
bool VNet::SendScene_Light(MObject& targetLight, bool remove) {

	try {

		if(!VUtil::IsLight(targetLight))
			throw tException();

		HeaderStructs::tSceneLight light;
		if(!GetNameBuffer(&light.target, targetLight))
			throw tException();

		if(!remove) {

			switch(targetLight.apiType()) {

				case MFn::kPointLight:
					light.type = 0;
					break;
				case MFn::kDirectionalLight:
					light.type = 1;
					break;
				case MFn::kSpotLight:
					light.type = 2;
					break;
				default:
					throw tException();
			}

			MFnDependencyNode lightDN(targetLight);
			ExtractProcedrualColorAttribute(lightDN, MString("color"), &light.color);
			ExtractProcedrualFloatAttribute(lightDN, MString("intensity"), &light.brightness);

			UINT emmitDiffuse;
			UINT emmitSpecular;

			ExtractProcedrualUintAttribute(lightDN, MString("emitDiffuse"), &emmitDiffuse);
			ExtractProcedrualUintAttribute(lightDN, MString("emitSpecular"), &emmitSpecular);

			light.specular = emmitSpecular;
			light.diffuse = emmitDiffuse;
		}

		light.remove = remove;
		if(!VNet::g_Client.SendDataPacket(&light, sizeof(HeaderStructs::tSceneLight), UPDATE_SCENELIGHTS))
			throw tException("Failed to send object removal message.");

		return true;
	}
	catch(tException exception) {
	
		return false;
	}
}

/**
	Sends the geometric data of the specified object to the server

	@author Dacre Denny
	@param targetObject the object to transmit the parameters of
	@param remove true to remove the object from the server
	@return true if successful otherwise false
*/
bool VNet::SendMesh_Geometry(MObject& targetObject, bool remove) { 

	char* pHeadBytes = NULL;
	
	try {

		if(!VUtil::IsMesh(targetObject))
			throw tException("Invalid parameter.");

		if(remove) {

			uint sendSize = sizeof(HeaderStructs::tGeometryHeader);
			pHeadBytes = new char [sendSize];
			if(!pHeadBytes)
				throw tException();
			ZeroMemory(pHeadBytes, sendSize);

			MString targetName;
			if(!VUtil::GetObjectName(targetObject, targetName))
				throw tException();
			
			HeaderStructs::tGeometryHeader *pHeader = (HeaderStructs::tGeometryHeader*)pHeadBytes;
			GetNameBuffer(&pHeader->target, targetName);
			pHeader->partCount = 0;
			pHeader->vertexFormat = 0;
			pHeader->remove = true;

			/*
			Send off the geometry message
			*/				
			if(!VNet::g_Client.SendDataPacket(pHeadBytes, sendSize, UPDATE_MESHGEOMETRY))
				throw tException("Failed to send remove geometry data.");

			if(pHeadBytes)
				delete [] pHeadBytes;
		}
		else {

			MPlug attribute;
			if(!VUtil::GetObjectAttribute(targetObject, MString("lv_format"), attribute))
				return false;

			int vertexFormat;
			if(MAYA_FAIL(attribute.getValue(vertexFormat)))
				throw tException("Failed to get valid vertex format.");

			//if(!VUtil::GetMeshVertexFormat(targetObject, vertexFormat))
			//	throw tException("Failed to get valid vertex format.");

			uint vertexSize = GetVertexFormatSize(vertexFormat);
			if(vertexSize == 0)
				throw tException("Invalid vertex size.");

			MStatus result;
			MFnMesh meshObject(targetObject, &result);
			
			MDagPath dagPath;
			if(MAYA_FAIL(meshObject.getPath(dagPath)))
				throw tException("Failed to get mesh dag path.");
			
			const uint instanceNumber = dagPath.instanceNumber();
			if(MAYA_FAIL(meshObject.syncObject()))
				throw tException("Failed to sync mesh.");
			if(MAYA_FAIL(meshObject.updateSurface()))
				throw tException("Failed to update mesh surface.");
			
			/*
			Get material data
			*/
			
			MObjectArray partArray;
			MIntArray partIndicies;
			if(MAYA_FAIL(meshObject.getConnectedShaders(instanceNumber, partArray, partIndicies)))
				throw tException("Failed to establish part count.");

			int partCount = VUtil::GetMeshPartCount(targetObject);
			if(partCount < 1)
				throw tException("Invalid target object.");

			/*
			Calculate the size of the data stream required
			*/
			MIntArray polygons;
			MIntArray indicies;
			if(MAYA_FAIL(meshObject.getTriangles(polygons, indicies)))
				throw tException("Failed to calculate triangle count.");
			
			uint triangleCount = 0;
			for(uint i = 0; i < polygons.length(); i++) 
				triangleCount += polygons[i];
			if(triangleCount == 0)
				throw tException("Invalid triangle count.");

			uint sendSize = sizeof(HeaderStructs::tGeometryHeader) + (partCount * sizeof(HeaderStructs::tGeometryPartHeader)) + (triangleCount * vertexSize * 3);
			pHeadBytes = new char [sendSize];
			if(!pHeadBytes)
				throw tException();
			ZeroMemory(pHeadBytes, sendSize);
			char* pWriteBytes = pHeadBytes;

			/*
			Get core geometric data
			*/
			MPointArray position;
			if(MAYA_FAIL(meshObject.getPoints(position,	MSpace::kObject)))
				throw tException("Failed to get position data.");

			/*
			Get data from color sets if required
			*/
			MString colorSetName = MString("");
			MString deformationSetName = MString("");
			if(vertexFormat & (VERTEX_COLOR | VERTEX_DEFORMATION)) {

				MStringArray sets;
				if(MAYA_FAIL(meshObject.getColorSetNames(sets)))
					throw tException("Failed to get color set names.");

				for(uint i = 0; i < sets.length(); i++) {
					
					if(colorSetName != MString("") && deformationSetName != MString(""))
						break;

					if((vertexFormat & VERTEX_DEFORMATION) && deformationSetName == MString("")) {

						if(sets[i] == MString("lv_defscale")) {
							deformationSetName = MString("lv_defscale");
							continue;
						}
					}
					
					if((vertexFormat & VERTEX_COLOR) && colorSetName == MString("")) {

						if(sets[i] != MString("lv_defscale")) {
							colorSetName = sets[i];
							continue;
						}
					}
				}
			}

			/*
			Get UV data if required
			*/
			MFloatArray uv0[2];
			MFloatArray uv1[2];
			MFloatArray uv2[2];
			MStringArray uvNames;
			if(vertexFormat & (VERTEX_UV0 | VERTEX_UV1 | VERTEX_UV2)) {

				if(MAYA_FAIL(meshObject.getUVSetNames(uvNames)))
					throw tException("Failed to get UV names.");

				switch(uvNames.length()) {

					case 0: {

						break;
					}
					default: case 3: {

						if((vertexFormat & VERTEX_UV2) && (MAYA_FAIL(meshObject.getUVs(uv2[0],uv2[1],&uvNames[2]))))
							throw tException("Failed to get UV channel: %i", 2);
					}
					case 2: {

						if((vertexFormat & VERTEX_UV1) && (MAYA_FAIL(meshObject.getUVs(uv1[0],uv1[1],&uvNames[1]))))
							throw tException("Failed to get UV channel: %i", 1);
					}
					case 1: {

						if((vertexFormat & VERTEX_UV0) && (MAYA_FAIL(meshObject.getUVs(uv0[0],uv0[1],&uvNames[0]))))
							throw tException("Failed to get UV channel: %i", 0);
					}
				}
			}

			/*
			Get other geometric data
			*/
			MFloatVectorArray tangent;
			if(vertexFormat & (VERTEX_TANGENT | VERTEX_UV0)) {

				if(MAYA_FAIL(meshObject.getTangents(tangent, MSpace::kObject, &uvNames[0])))
					throw tException("Failed to get tangent vectors.");
			}

			/*
			Write header information of this mesh into the stream
			*/
			HeaderStructs::tGeometryHeader *pHeader = (HeaderStructs::tGeometryHeader*)pWriteBytes;
			pWriteBytes += sizeof(HeaderStructs::tGeometryHeader);
			MString targetName;
			if(!VUtil::GetObjectName(targetObject, targetName))
				throw tException();

			std::set<uint> currentMaterials;

			/*
			For every part in the object,
			*/
			MItMeshPolygon polygonIterate(targetObject);
			for(uint i = 0; i < partCount; i++) {	
		
				/*
				Set up the header for this particular part in the stream
				*/
				HeaderStructs::tGeometryPartHeader* pPartHeader = (HeaderStructs::tGeometryPartHeader*)pWriteBytes;
				pWriteBytes += sizeof(HeaderStructs::tGeometryPartHeader);

				/*
				go through every polygon in the object,
				*/
				uint polygonCount = polygons.length();
				for(uint j = 0; j < polygonCount; j++) {

					/*
					and look for polygons the match the material of the part we are currently on.
					*/
					int polygonIndex = polygonIterate.index();
					int polygonPartIndex = partIndicies[polygonIndex];
					if(i == polygonPartIndex) {

						int polygonTriangleCount = polygons[j];
						
						/*
						The call to getConnectedShaders was used to get the current materials assigned to
						this mesh. Shaders can be connected even if no polygons have that shader applied.
						This therefore makes the number of connected shaders invalid under certian circumstances
						when we need the total number of materials that exist on the geoemtry
						*/
						if(polygonTriangleCount > 0)
							currentMaterials.insert(i);

						/*
						Write back into the previous header of memory and incremented triangle count
						*/
						pPartHeader->triangleCount += polygonTriangleCount;

						/*
						When we have found a polygon of the current part/material, go through each
						of it's triangles (n triangles for a polygon)
						*/
						for(uint k = 0; k < polygonTriangleCount; k++) {

							int vertexIndicies[3] = {0};
							if(MAYA_FAIL(meshObject.getPolygonTriangleVertices(j, k, vertexIndicies)))
								throw tException("Failed to get polygon trjangle vertices.");

							uint polyVertexCount = polygonIterate.polygonVertexCount();
							for(uint m = 0; m < polyVertexCount; m++) {
								
								int vertexIndex = polygonIterate.vertexIndex(m);
								char* pWriteVertex = NULL;
								if(vertexIndex == vertexIndicies[0])
									pWriteVertex = pWriteBytes + (0 * vertexSize);
								else if(vertexIndex == vertexIndicies[1])
									pWriteVertex = pWriteBytes + (1 * vertexSize);
								else if(vertexIndex == vertexIndicies[2])
									pWriteVertex = pWriteBytes + (2 * vertexSize);
								else
									continue;

								/*
								If required, extract and write position
								*/
								if(vertexFormat & VERTEX_POSITION) {

									float temp[4] = {0.0f, 0.0f, 0.0f, 0.0f};
									if(vertexIndex < position.length())
										position[vertexIndex].get(temp);

									tVector3* pPosition = (tVector3*)pWriteVertex;
									pWriteVertex += sizeof(tVector3);

									pPosition->x = temp[0];
									pPosition->y = temp[1];
									pPosition->z = temp[2];
								}

								/*
								If required, extract and write normal
								*/
								if(vertexFormat & VERTEX_NORMAL) {

									MVector normal;
									polygonIterate.getNormal(m, normal, MSpace::kObject);

									tVector3* pNormal = (tVector3*)pWriteVertex;
									pWriteVertex += sizeof(tVector3);

									pNormal->x = normal.x;
									pNormal->y = normal.y;
									pNormal->z = normal.z;
								}
								
								/*
								If required, extract and write tangent
								*/
								if(vertexFormat & VERTEX_TANGENT) {

									float temp[4] = {0.0f, 0.0f, 0.0f, 0.0f};
									int	uvIndex = 0;
									polygonIterate.getUVIndex(m, uvIndex, &uvNames[0]);
									if(uvIndex < tangent.length())
										tangent[uvIndex].get(temp);

									tVector3* pTangent = (tVector3*)pWriteVertex;
									pWriteVertex += sizeof(tVector3);

									pTangent->x = temp[0];
									pTangent->y = temp[1];
									pTangent->z = temp[2];
								}

								/*
								If required, extract and write uv coordinates
								*/
								if(vertexFormat & VERTEX_UV0) {

									tVector2* pUV = (tVector2*)pWriteVertex;
									pWriteVertex += sizeof(tVector2);

									int	uvIndex = 0;
									polygonIterate.getUVIndex(m, uvIndex, &uvNames[0]);
									if(uvIndex < uv0[0].length() && uvIndex < uv0[1].length()) {

										pUV->x = uv0[0][uvIndex];
										pUV->y = -uv0[1][uvIndex];
									}
								}

								/*
								If required, extract and write uv coordinates
								*/
								if(vertexFormat & VERTEX_UV1) {

									tVector2* pUV = (tVector2*)pWriteVertex;
									pWriteVertex += sizeof(tVector2);

									int	uvIndex = 0;
									polygonIterate.getUVIndex(m, uvIndex, &uvNames[1]);
									if(uvIndex < uv1[0].length() && uvIndex < uv1[1].length()) {

										pUV->x = uv1[0][uvIndex];
										pUV->y = -uv1[1][uvIndex];
									}
								}

								/*
								If required, extract and write uv coordinates
								*/							
								if(vertexFormat & VERTEX_UV2) {

									tVector2* pUV = (tVector2*)pWriteVertex;
									pWriteVertex += sizeof(tVector2);

									int	uvIndex = 0;
									polygonIterate.getUVIndex(m, uvIndex, &uvNames[2]);
									if(uvIndex < uv2[0].length() && uvIndex < uv2[1].length()) {

										pUV->x = uv2[0][uvIndex];
										pUV->y = -uv2[1][uvIndex];
									}
								}
								
								/*
								If required, extract and write color
								*/
								if(vertexFormat & VERTEX_COLOR) {

									tVector4* pColor = (tVector4*)pWriteVertex;
									pWriteVertex += sizeof(tVector4);

									int colorIndex = 0;
									MColor color;
									polygonIterate.getColorIndex(m, colorIndex, &colorSetName);
									meshObject.getColor(colorIndex, color, &colorSetName);

									pColor->x = color.r;
									pColor->y = color.g;
									pColor->z = color.b;
									pColor->w = color.a;
								}

								/*
								If required, extract and write deformation
								*/
								if(vertexFormat & VERTEX_DEFORMATION) {

									tVector2* pDeform = (tVector2*)pWriteVertex;
									pWriteVertex += sizeof(tVector2);

									int deformIndex = 0;
									MColor deform;
									polygonIterate.getColorIndex(m, deformIndex, &deformationSetName);
									meshObject.getColor(deformIndex, deform, &deformationSetName);

									pDeform->x = (deform.r + deform.g + deform.b) * 0.33f;
									pDeform->y = 0.0f;
								}			
							}
							
							/*
							Increment writer head forward by three vertices to the next triangle
							*/
							pWriteBytes += (vertexSize * 3);
						}
					}

					if(MAYA_FAIL(polygonIterate.next()))
						throw tException("Failed to increment polygon iterator.");
				}

				polygonIterate.reset();
			}

			GetNameBuffer(&pHeader->target, targetName);
			pHeader->partCount = currentMaterials.size();
			pHeader->vertexFormat = vertexFormat;
			pHeader->remove = false;
			

			/*
			Send off the geometry data and clean up heap as nessisary
			*/				
			if(!VNet::g_Client.SendDataPacket(pHeadBytes, sendSize, UPDATE_MESHGEOMETRY))
				throw tException("Failed to send geometry data.");

			if(pHeadBytes)
				delete [] pHeadBytes;
		}
		return true;
	}
	catch(tException exception) {

		if(pHeadBytes)
			delete [] pHeadBytes;
		return false;
	}
}

tCharBuffer NameBuffer(MString string)
{
	tCharBuffer result;
	const char* c_str = string.asChar();
	if(c_str) {
		memset(result.Buffer, (int)'\0', 32);
		memcpy(result.Buffer, c_str, strlen(c_str));
	}
	return result;
}

/**
	Sends the current selected object data of the specified scene object to the viewer

	@author Dacre Denny
	@param targetObjects the object array representing the current selection list
	@return true if successful otherwise false
*/
bool VNet::SendScene_Selected(MObjectArray& targetObjects) {

	char* pData = NULL;

	try {
		
		u_int allocSize = sizeof(uint) + sizeof(tCharBuffer);
		pData = new byte [allocSize];
		tCharBuffer* pWritePointer = (tCharBuffer*)(pData + sizeof(uint));

		for(uint i = 0; i < targetObjects.length(); i++) {
			
			if(!GetNameBuffer((tCharBuffer*)pWritePointer, targetObjects[i]))
				throw tException("Failed to aquire name.");
			pWritePointer++;
		}
		
		if(!g_Client.SendDataPacket(pData, allocSize, UPDATE_SELECTION))
			throw tException("Failed to send transformation.");

		return true;
	} 
	catch(tException exception) {
	
		if(pData)
			delete [] pData;

		return false;
	}	
}

/**
	Sends instancing data of the specified scene object to the viewer

	@author Dacre Denny
	@param targetObject the object who's instancing data is being sent
	@return true if successful otherwise false
*/
//bool VNet::SendInstancing(MObject& targetObject) {
//	
//	char* pHeadBytes = NULL;
//
//	try {
//
//		/*
//		Check basic preconditions
//		*/
//		if(!VUtil::IsMesh(targetObject))
//			throw tException("Invalid parameters.");
//
//		MPlug attribute;
//		if(!VUtil::GetObjectAttribute(targetObject, MString("lv_instchildren"), attribute))
//			throw tException("Target object has required attribute missing.");
//
//		/*
//		Get the information strings about each instance assigned to this target object.
//		If no information is gathered, return true - we assume no instancing has been assigned
//		by the user
//		*/
//		MString instanceChildrenString = attribute.asString();
//		MStringArray instanceChildrenArray;
//		instanceChildrenString.split(';',instanceChildrenArray);
//
//		uint instanceChilrenCount = instanceChildrenArray.length();
//
//		/*
//		Calculate the size of, and allocated the data stream to send the data in.
//		*/
//		uint sendSize = /*sizeof(HeaderStructs::tInstanceChildHeader) + 
//			instanceChilrenCount * sizeof(HeaderStructs::tInstanceChildHeader)*/0;
//
//		pHeadBytes = new char [sendSize];
//		if(!pHeadBytes)
//			throw tException();
//		char* pWriteBytes = pHeadBytes;
//
//		/*
//		Write header information about the instancing data into the beginning of the stream
//		*/
//		HeaderStructs::tInstanceHeader* pHeader = (HeaderStructs::tInstanceHeader*)pWriteBytes;
//		pWriteBytes += sizeof(HeaderStructs::tInstanceHeader);
//		pHeader->instanceCount = instanceChilrenCount;
//		if(!GetNameBuffer(&pHeader->target, targetObject))
//			throw tException();
//
//		/*
//		The first itteration of two required.
//		Extract the instancing information that actually exists in the target object and embed this
//		data into the send stream. This will ensure that the object on the server is able to match
//		incoming samplers with instance children etc
//		*/
//		for(uint i = 0; i < instanceChilrenCount; i++) {
//
//			//HeaderStructs::tInstanceChildHeader* pChildHeader = (HeaderStructs::tInstanceChildHeader*)pWriteBytes;
//			//pWriteBytes += sizeof(HeaderStructs::tInstanceChildHeader);
//
//			///*
//			//Break the child string into it's data pieces. This allows access to the name of the child 
//			//object as well as the names of the samplers used to control the instancing
//			//*/
//			//MString instanceChildString = instanceChildrenArray[i];
//			//MStringArray instanceChildArray;
//			//instanceChildString.split(',', instanceChildArray);
//
//			///*
//			//Ensure the data is avalible and write it into the send stream
//			//*/
//			//if(instanceChildArray.length() != 5)
//			//	continue;
//
//			//MString instanceChildName = instanceChildArray[0];
//			//MString samplerPosition = instanceChildArray[1];
//			//MString samplerRotation = instanceChildArray[2];
//			//MString samplerScale = instanceChildArray[3];
//			//MString cookieFilename = instanceChildArray[4];
//
//			//GetNameBuffer(&pChildHeader->child, instanceChildName);
//			//GetNameBuffer(&pChildHeader->samplerPosition, samplerPosition);
//			//GetNameBuffer(&pChildHeader->samplerScale, samplerScale);
//			//GetNameBuffer(&pChildHeader->samplerRotation, samplerRotation);
//			//GetNameBuffer(&pChildHeader->cookieFilename, cookieFilename);
//		}
//
//		/*
//		Send the instancing data about the target object to the server and clean up as nessisary
//		*/
//		//if(!g_Client.SendDataPacket((LPVOID)pHeadBytes, sendSize, UPDATE_MESHINSTANCING))
//		//	throw tException("Failed to send instancing data.");
//
//		if(pHeadBytes)
//			delete [] pHeadBytes;
//
//		/*
//		The second iteration over the child instance.
//		We now go through each and ensure that the child geometry exists on the server. Also take this
//		oppourtunity to send through sampler information that is used to control the way and instance
//		appears over the target object
//		*/
//		for(uint i = 0; i < instanceChilrenCount; i++) {
//
//			/*
//			Again break the child string into it's data pieces as before
//			*/
//			MString instanceChildString = instanceChildrenArray[i];
//			MStringArray instanceChildArray;
//			instanceChildString.split(',', instanceChildArray);
//
//			if(instanceChildArray.length() != 5)
//				continue;
//
//			MString instanceChildName = instanceChildArray[0];
//
//			/*
//			Query the server to determine if the child mesh even exists on it.
//			If it is not found then we need to send it through so that instancing will occour on the 
//			server. Here a query specific to these needs is built, sent and then checked
//			*/
//			char* pResponceData = NULL;
//			uint responceSize = 0;
//			QuerysStructs::tViewerInfoQuery queryChild;
//			queryChild.responceType = QUERY_RESPONCE_OBJECTEXISTS;
//			GetNameBuffer(&queryChild.target, instanceChildName);
//			if(!VNet::QueryServer(&queryChild, &pResponceData, &responceSize))
//				continue;
//
//			/*
//			Determine if the child geoemtry even needs to be sent. Tidy up query data that is
//			no longer needed too.
//			*/
//			QuerysStructs::tQueryHeader* pQuery = (QuerysStructs::tQueryHeader*)pResponceData;
//			bool sendChild = (IsEqual(pQuery->targetName.Buffer, instanceChildName) == false || pQuery->isPresent == false);
//			delete [] pResponceData;
//
//			/*
//			The child wasn't found on the server so send it
//			*/
//			if(sendChild) {
//				
//				MObject instanceChildObject;
//				if(!VUtil::GetMeshByName(instanceChildName, instanceChildObject))
//					continue;			
//			
//				if(!VSyncViewer::IsObjectOnServer(instanceChildObject))
//					if(!VNet::SendMesh_Geometry(instanceChildObject, true))
//						throw tException("Failed to send child object geometry: %s", instanceChildName.asChar());
//
//				//if(!VNet::SendMesh_Parameters(instanceChildObject))
//				//	throw tException("Failed to send child object parameters: %s", instanceChildName.asChar());
//
//				if(!VNet::SendDeformation(instanceChildObject))
//					throw tException("Failed to send child object deformation: %s", instanceChildName.asChar());
//			}
//
//			/*
//			Update the samplers associated with this instance child. 
//			Send each channel to the server. Some sampler names may be
//			invalid (ie "") which is ok, because the user may not be
//			making explicit use of them.
//			*/
//			MString samplerPosition = instanceChildArray[1];
//			MString samplerRotation = instanceChildArray[2];
//			MString samplerScale = instanceChildArray[3];
//
//			MObject samplerObject;
//			if(VUtil::GetSamplerByName(samplerPosition, samplerObject)) {
//
//				if(!VNet::SendSampler(targetObject, samplerObject, instanceChildName, MString("liveview_position"), false))
//					throw tException("Failed to send position sampler '%s' for instancing of '%'", 
//					samplerPosition.asChar(),
//					instanceChildName.asChar());
//			}
//			if(VUtil::GetSamplerByName(samplerRotation, samplerObject)) {
//
//				if(!VNet::SendSampler(targetObject, samplerObject, instanceChildName, MString("liveview_rotation"), false))
//					throw tException("Failed to send rotation sampler '%s' for instancing of '%'", 
//					samplerPosition.asChar(),
//					instanceChildName.asChar());
//			}
//			if(VUtil::GetSamplerByName(samplerScale, samplerObject)) {
//
//				if(!VNet::SendSampler(targetObject, samplerObject, instanceChildName, MString("liveview_scale"), false))
//					throw tException("Failed to send scale sampler '%s' for instancing of '%'", 
//					samplerPosition.asChar(),
//					instanceChildName.asChar());
//			}
//		}
//
//		return true;
//	} 
//	catch(tException exception) {
//	
//		if(pHeadBytes)
//			delete [] pHeadBytes;
//
//		return false;
//	}	
//}

/**
	Sends the transformation data of the specified scene object to the viewer

	@author Dacre Denny
	@param targetObject the object that is targeted by the specified transformation
	@param transformObject object containing the transformation to apply
	@return true if successful otherwise false
*/
bool VNet::SendTransform(MObject& targetObject, MObject& transformObject)
{
	byte* pData = NULL;
	try {

		if(/*!VUtil::IsMesh(targetObject) || */!VUtil::IsTransform(transformObject))
			throw tException("Invalid parameter.");

		const uint size = (sizeof(float) * 16) + sizeof(tCharBuffer);
		char data[size];
		char* pWriteHeader = &data[0];
		if(!GetNameBuffer((tCharBuffer*)data, targetObject))
			throw tException("Failed to aquire name.");
		
		pWriteHeader += sizeof(tCharBuffer);

		MStatus result;
		MFnTransform transform(transformObject, &result);
		if(MAYA_FAIL(result))
			throw tException("Cast error.");

		MTransformationMatrix transformationMatrix = transform.transformation(&result);
		if(MAYA_FAIL(result))
			throw tException("Cast error.");

		float temp[4][4] = {0.0f};
		if(MAYA_FAIL(transformationMatrix.asMatrix().get(temp)))
			throw tException("Data extraction error.");
		memcpy(pWriteHeader, temp, sizeof(float) * 16);
		
		if(!g_Client.SendDataPacket(&data[0], size, UPDATE_TRANSFORM))
			throw tException("Failed to send transformation.");
		
		return true;

	} catch(tException exception) {

		VPlugin::HandleException(&exception);
		return false;	
	}
}

bool VNet::QueryServer(QuerysStructs::tViewerInfoQuery* pQuery, char** pDataOut, uint *pDataSize) {

	if(!pDataOut || !pDataSize || !pQuery || !VNet::IsConnected())
		return false;

	int responceTimeout = 100;
	g_Client.SendDataPacket(pQuery, sizeof(QuerysStructs::tViewerInfoQuery), pQuery->responceType);
	
	while(!g_Client.HasRecievedPackets() && responceTimeout > 0) {
		
		responceTimeout--;
		Sleep(25);
	}

	if(!g_Client.HasRecievedPackets())
		return false;

	responceTimeout = 100;
	std::vector<Connection::Packet> packetList;
	while(responceTimeout > 0) {

		if(g_Client.GetDataPackets(&packetList)) {

			for(std::vector<Connection::Packet>::iterator i = packetList.begin(); i != packetList.end() && responceTimeout > 0; i++) {
			
				if(i->header.packetID == pQuery->responceType) {
					
					uint dataSize = i->header.dataSize;
					if(dataSize == 0) {

						*pDataOut = NULL;
						pDataSize = 0;
					}
					else {

						char* pDataBuffer = new char [dataSize];
						memcpy(pDataBuffer, (LPVOID)i->pData, dataSize);
						*pDataSize = dataSize;
						*pDataOut = pDataBuffer;
					}
					
					/*
					Breaks the iterations
					*/
					responceTimeout = 0;
				} 

				i->ReleaseHeapData();
			}
		}
		
		responceTimeout--;
//		Sleep(5);
		packetList.clear();
	}
	return true;
}

bool VNet::IsConnected() {

	return VNet::g_Client.IsConnected();
}

MString VNet::Status() {

	return VNet::g_Status;
}

DWORD WINAPI VNet::MonitorConnection(LPVOID) {

	while(VNet::g_isMonitoring) {
		
		if(!VNet::g_Client.IsConnected()) {
			
			VNet::g_Client.Release();
			VNet::g_Status = "Server disconnected";

			VNet::g_isMonitoring = false;
			VGUI::Refresh::ViewServer();
		}

		Sleep(150);
	}

	return 0;
}

bool VNet::Connect() {

	try {

		if(VNet::IsConnected()) {

			VNet::g_Status = "Already connected to: " + VNet::HostAddress + "";
		}
		else {
		
			if(!VNet::g_Client.Initalize(VNet::HostAddress.asChar(), true))
				throw tException("Failed to connect to: %s", VNet::HostAddress.asChar());

			if(VNet::g_hMontiorConnection) {

				VNet::g_isMonitoring = false;
				WaitForSingleObject(VNet::g_hMontiorConnection, 2500);
				CloseHandle(VNet::g_hMontiorConnection);
			}
		
			VNet::g_isMonitoring = true;
			VNet::g_hMontiorConnection = CreateThread(NULL, 0,
				VNet::MonitorConnection, NULL, 0, NULL);
			if(!VNet::g_hMontiorConnection)
				throw tException("Failed to create monitor thread");

			VNet::g_Status = "Connected";
		}
		
		return true;

	}catch(tException exception) {

		VNet::Disconnect();

		VNet::g_isMonitoring = false;
		if(VNet::g_hMontiorConnection) {
			WaitForSingleObject(VNet::g_hMontiorConnection, 2500);
			CloseHandle(VNet::g_hMontiorConnection);
			VNet::g_hMontiorConnection = NULL;
		}

		std::string message;
		tException::GetMessage(&exception, message);
		VNet::g_Status = "Failed to connect";

		VPlugin::HandleException(&exception);
		return false;
	}
}

bool VNet::Disconnect() {

	if(VNet::IsConnected()) {

		if(VNet::g_Client.Release()) {

			VNet::g_Status = "Disconnected";
		}
		else {

			VNet::g_Status = "Failed to disconnect";
			return false;
		}
	}
	else {

		VNet::g_Status = "No current connection";
	}

	VNet::g_isMonitoring = false;
	if(VNet::g_hMontiorConnection) {

		WaitForSingleObject(VNet::g_hMontiorConnection, 2500);
		CloseHandle(VNet::g_hMontiorConnection);
		VNet::g_hMontiorConnection = NULL;
		
	}

	return true;
}


